<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="../css/main.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/highlight.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/print.css" media="print" rel="stylesheet" type="text/css" />
<title>第15章 - ユニットテストと機能テスト</title>
</head>

<body>
<div class="navigation">

<table width="100%">
<tr>
<td width="40%" align="left"><a href="14-Generators.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="16-Application-Management-Tools.html">次の章</a></td>
</tr>
</table>
<hr/>
</div>

<div>
<a name="chapter.15.unit.and.functional.testing" id="chapter.15.unit.and.functional.testing"></a><h1>第15章 - ユニットテストと機能テスト</h1>

<p>自動テストはプログラミングにおけるオブジェクト指向以降の最大の進歩の1つです。とりわけ、Webアプリケーションを開発するための助けになるので、例えおびただしい数のアプリケーションがリリースされたとしても、アプリケーションの品質を保証できます。symfonyは自動テストを円滑に運用するためのさまざまなツールを提供し、この章ではそれらを紹介します。</p>

<div class="toc">
<dl>
<dt><a href="#automated.tests">15.1. 自動ツール</a></dt>
<dd><dl>
<dt><a href="#unit.and.functional.tests">15.1.1. ユニットテストと機能テスト</a></dt>
<dt><a href="#testdriven.development">15.1.2. テスト駆動開発</a></dt>
<dt><a href="#the.lime.testing.framework">15.1.3. limeテストフレームワーク</a></dt>
</dl></dd>
<dt><a href="#unit.tests">15.2. ユニットテスト</a></dt>
<dd><dl>
<dt><a href="#what.do.unit.tests.look.like">15.2.1. ユニットテストは何に見えますか？</a></dt>
<dt><a href="#unit.testing.methods">15.2.2. ユニットテストのメソッド</a></dt>
<dt><a href="#testing.parameters">15.2.3. パラメーターをテストする</a></dt>
<dt><a href="#the.testunit.task">15.2.4. test-unitタスク</a></dt>
<dt><a href="#stubs.fixtures.and.autoloading">15.2.5. スタブ、フィクスチャ、オートロード</a></dt>
</dl></dd>
<dt><a href="#functional.tests">15.3. 機能テスト</a></dt>
<dd><dl>
<dt><a href="#what.do.functional.tests.look.like">15.3.1. 機能テストはどのように見えますか？</a></dt>
<dt><a href="#browsing.with.the.sftestbrowser.object">15.3.2. sfBrowserオブジェクトでブラウジングする</a></dt>
<dt><a href="#using.assertions">15.3.3. アサーションを使う</a></dt>
<dt><a href="#using.css.selectors">15.3.4. CSSセレクタを使う</a></dt>
<dt><a href="#working.in.the.test.environment">15.3.5. テスト環境でとり組む</a></dt>
<dt><a href="#the.testfunctional.task">15.3.6. test-functionalタスクを使う</a></dt>
</dl></dd>
<dt><a href="#test.naming.practices">15.4. テストの命名慣習</a></dt>
<dt><a href="#special.testing.needs">15.5. 特別なテストのニーズ</a></dt>
<dd><dl>
<dt><a href="#executing.tests.in.a.test.harness">15.5.1. テストハーネスでテストを実行する</a></dt>
<dt><a href="#accessing.a.database">15.5.2. データベースにアクセスする</a></dt>
<dt><a href="#testing.the.cache">15.5.3. キャッシュをテストする</a></dt>
<dt><a href="#testing.interactions.on.the.client">15.5.4. クライアント上のインタラクションをテストする</a></dt>
</dl></dd>
<dt><a href="#summary">15.6. まとめ</a></dt>
</dl>
</div>
<a name="automated.tests" id="automated.tests"></a><h2>自動ツール</h2>

<p>Webアプリケーションを開発した経験を持つ開発者はテストを実施するために時間がかかることを承知しています。テストケースを書き、それらを実施して、結果を解析することは退屈な作業です。加えて、Webアプリケーションの要件はつねに変化しがちなので、コードのリファクタリングとアプリケーションのリリースが継続して行われることになります。この作業の流れでは、予期しない新たなエラーが定期的に起こりがちです。</p>

<p>なぜ自動化されたツールが、必要ではなくても提案され、成功した開発環境の一部になっている理由はそういうわけです。テストケースのセットはアプリケーションが実際に行うことを保証します。内部のコードが頻繁に書き直される場合、自動化されたテストは予想外の回帰を防止します。加えて、厳格な標準フォーマットによって、テストフレームワークが理解しやすいようにテストを書くことを開発者に強制します。</p>

<p>自動テストは時に開発者のドキュメントに取って代わります。アプリケーションが行うことの説明になっているからです。よいテストスイートはテスト入力のセットのためにどんな出力が期待されているのかを示し、メソッドの目的を説明するよい方法です。</p>

<p>symfonyフレームワークはこの原則を自分自身に適用します。symfonyの内部は自動テストによって検証されます。これらのユニットテスト(unit test)と機能テスト(functional test)はsymfonyの標準的な配布物には搭載されていませんが、SVNリポジトリからチェックアウトするか、オンラインの<a href="http://trac.symfony-project.org/browser/branches/1.0/test">http://trac.symfony-project.org/browser/branches/1.0/test</a>で閲覧できます。</p>

<a name="unit.and.functional.tests" id="unit.and.functional.tests"></a><h3>ユニットテストと機能テスト</h3>

<p>ユニットテスト(unit test)は単一のコードコンポーネントが任意の入力に対して正しい出力を提供することを確認します。これらのテストは関数とメソッドがすべての特定のケースで動作する方法を検証します。ユニットテストは一度に1つのケースを処理するので、たとえば、1つのメソッドが特定の状況で異なる動作をする場合、いくつかのユニットテストが必要になることがあります。</p>

<p>機能テスト(functional test)は、シンプルな入力から出力への変換ではなく、完全な機能を検証します。たとえば、キャッシュシステムは機能テストだけで検証できます。なぜなら複数のステップが含まれるからです: 最初、ページがリクエストされ、レンダリングされます; つぎに、キャッシュからページが取得されます。ですので機能テストはプロセスを検証し、シナリオを必要とします。symfonyにおいて、すべてのアクションに対して機能テストを書くべきです。</p>

<p>もっと複雑なインタラクションのには、これらの2つのタイプのテストは不十分かもしれません。たとえば、AjaxのインタラクションはJavaScriptを実行するためにWebブラウザーを必要とするので、これらを自動的にテストするには特別なサードパーティのツールが必要です。さらに、視覚効果を検証できるのは人間だけです。</p>

<p>自動ツールへの広い範囲でのアプローチがある場合、おそらく、これらすべての方法の組み合わせを使う必要があります。指針としては、テストをシンプルで読みやすいものに保つべきであることを覚えておいてください。</p>

<blockquote class="note"><p>
  自動テストは結果と予期される出力の比較によって動作します。言い換えると、アサーション(<code>$a == 2</code>などの式)を評価します。アサーションの値は<code>true</code>もしくは<code>false</code>で、テストが成功したか失敗したかを判定します。自動テストの技術を扱うとき"アサーション"(assertion)という言葉は一般的に使われます。</p>
</blockquote>

<a name="testdriven.development" id="testdriven.development"></a><h3>テスト駆動開発</h3>

<p>テスト駆動開発(TDD - Test-Driven Development)の方法論において、テストはコードのまえに書かれます。最初にテストを書くことは実際に開発するまえに機能が実現するタスクに焦点を当てるための助けになります。これは、エクストリームプログラミング(XP Extreme Programming)のような、よい習慣で、同様にお勧めです。加えて、この方法論はユニットテストを最初に書いておかないとあとで書くことはないという事実を考慮しています。</p>

<p>たとえば、テキストをとり除く機能を開発しなければならない場合を考えてみましょう。この機能は文字列の最初と最後の空白スペースをとり除き、アルファベットでない文字をアンダースコアに置き換え、すべての大文字を小文字に変換します。テスト駆動開発において、テーブル15-1で示されるように、すべてのあり得る場合を考え、それぞれの場合に対して入力の例と期待される出力を準備することになります。</p>

<p>テーブル15-1 想定されるテキストをとり除く機能</p>

<table>
<thead>
<tr>
  <th>入力</th>
  <th>期待される出力t</th>
</tr>
</thead>
<tbody>
<tr>
  <td><code>" foo "</code></td>
  <td><code>"foo"</code></td>
</tr>
<tr>
  <td><code>"foo bar"</code></td>
  <td><code>"foo_bar"</code></td>
</tr>
<tr>
  <td><code>"-)foo:..=bar?"</code></td>
  <td><code>"__foo____bar_"</code></td>
</tr>
<tr>
  <td><code>"FooBar"</code></td>
  <td><code>"foobar</code>"</td>
</tr>
<tr>
  <td><code>"Don't foo-bar me!"</code></td>
  <td><code>"don_t_foo_bar_me_"</code></td>
</tr>
</tbody>
</table>

<p>ユニットテストを書きたい場合、それらを実行して、失敗する様子を見てください。最初のテストケースを処理するために必要なコードを追加し、テストを再度動かし、最初のテストが成功するのを見て、そのように続けます。最終的に、すべてのテストケースは成功したとき、機能は正しいです。</p>

<p>テスト駆動方法論で開発されたアプリケーションは大まかに実際のコードと同じぐらいのテストコードで終わります。テストケースをデバッグすることに時間を費やしたくないのであれば、それらをシンプルに保ってください。</p>

<blockquote class="note"><p>
  メソッドをリファクタリングすると以前は現れなかった新しいバグが作られる可能性があります。運用環境に新しいリリースのアプリケーションをデプロイするまえに、すべての自動テストを実行することもよい習慣であるのはそういうわけです。これは回帰テスト(regression testing)と呼ばれます。</p>
</blockquote>

<a name="the.lime.testing.framework" id="the.lime.testing.framework"></a><h3>limeテストフレームワーク</h3>

<p>PHPの世界においてユニットテストのフレームワークは多く存在し、PhpUnitとSimpleTestがもっともよく知られています。symfonyはlimeと呼ばれる独自のテストフレームワークを持ちます。Perlライブラリの<code>Test::More</code>に基づき、TAP(Test Anything Protoco)に準拠しています、このことは、テストの出力をより読みやすくするために設計されたTAPで定められているように、テストの結果が表示されることを意味します。</p>

<p>limeはユニットテストのサポートを提供します。PHPのテストフレームワークよりも軽量でいくつかの利点があります:</p>

<ul>
<li>limeはそれぞれの動くテストの間の奇妙な副作用を回避するためにテストファイルをサンドボックスで起動します。</li>
<li>limeテストおよびそのテストの出力はとても読みやすいです。互換性のあるシステム上で、limeは重要な情報を識別するためにスマートな方法でカラー出力を使います。</li>
<li>回帰テストのためにsymfony自身が<code>lime</code>テストを使うので、ユニットテストと回帰テストの多くの例がsymfonyのソースコードで見つかります。</li>
<li>limeのコアはユニットテストによって検証されます。</li>
<li>limeはPHPで書かれており、速く動作し上手に書かれています。limeは依存なしで単独の<code>lime.php</code>ファイルに含まれます。</li>
</ul>

<p>つぎのセクションで説明されるさまざまなテストはlimeの構文を使います。symfonyをインストールしたのであればこれらのテストはそのまま動きます。</p>

<blockquote class="note"><p>
  運用サーバーでユニットテストと機能テストを起動させることは想定されていません。これらのテストは開発者のツールなので、ホストサーバーではなく、開発者のコンピュータで動かすべきです。</p>
</blockquote>

<a name="unit.tests" id="unit.tests"></a><h2>ユニットテスト</h2>

<p>symfonyのユニットテストは<code>Test.php</code>で終わるシンプルなPHPのファイルで、アプリケーションの<code>test/unit/</code>ディレクトリに設置されています。これらはシンプルで読みやすい構文に従います。</p>

<a name="what.do.unit.tests.look.like" id="what.do.unit.tests.look.like"></a><h3>ユニットテストは何に見えますか？</h3>

<p>リスト15-1は<code>strtolower()</code>関数のための典型的なユニットテストの一式を示しています。このテストは<code>lime_test</code>オブジェクトをインスタンス化することで始まります(今はパラメーターに悩む必要はありません)。それぞれのユニットテストは<code>lime_test</code>インスタンスへの呼び出しです。これらのメソッドの最後のパラメーターはつねに出力として提供されるオプションの文字列です。</p>

<p>リスト15-1 - ユニットテストのファイルの例(<code>test/unit/strtolwerTest.php</code>)</p>

<pre class="php"><span class="kw2">&lt;?php</span>
&nbsp;
<span class="kw1">include</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../bootstrap/unit.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">require_once</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../../lib/strtolower.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$t</span> <span class="sy0">=</span> <span class="kw2">new</span> lime_test<span class="br0">&#40;</span><span class="nu0">7</span><span class="sy0">,</span> <span class="kw2">new</span> lime_output_color<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// strtolower()</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">diag</span><span class="br0">&#40;</span><span class="st_h">'strtolower()'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">isa_ok</span><span class="br0">&#40;</span><span class="kw3">strtolower</span><span class="br0">&#40;</span><span class="st_h">'Foo'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'string'</span><span class="sy0">,</span>
    <span class="st_h">'strtolower()は文字列を返す'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="kw3">strtolower</span><span class="br0">&#40;</span><span class="st_h">'FOO'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'foo'</span><span class="sy0">,</span>
    <span class="st_h">'strtolower()は入力を小文字に変換する'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="kw3">strtolower</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'foo'</span><span class="sy0">,</span>
    <span class="st_h">'strtolower()は小文字を変更しない'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="kw3">strtolower</span><span class="br0">&#40;</span><span class="st_h">'12#?@~'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'12#?@~'</span><span class="sy0">,</span>
    <span class="st_h">'strtolower()はアルファベットではない文字を変更しない'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="kw3">strtolower</span><span class="br0">&#40;</span><span class="st_h">'FOO BAR'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'foo bar'</span><span class="sy0">,</span>
    <span class="st_h">'strtolower()は空白をそのままにする'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="kw3">strtolower</span><span class="br0">&#40;</span><span class="st_h">'FoO bAr'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'foo bar'</span><span class="sy0">,</span>
    <span class="st_h">'strtolower()は混合する文字の入力を扱う'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="kw3">strtolower</span><span class="br0">&#40;</span><span class="st_h">''</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'foo'</span><span class="sy0">,</span>
    <span class="st_h">'strtolower()は空の文字列をfooに変換する'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>コマンドラインから<code>test-unit</code>タスクでテストセットを起動させてください。コマンドラインの出力内容はとても明確なので、成功したテストと失敗したテストを見つけ出すための助けになります。リスト15-2の例のテストの出力をご覧ください。</p>

<p>リスト15-2 - 1つのユニットテストをコマンドラインから起動させる</p>

<pre><code>&gt; symfony test-unit strtolower

1..7
# strtolower()
ok 1 - strtolower()は文字列を返す
ok 2 - strtolower()は入力を小文字に変換する
ok 3 - strtolower()は小文字を変更しない
ok 4 - strtolower()はアルファベットではない文字を変更しない
ok 5 - strtolower()は空白をそのままにする
ok 6 - strtolower()は混合する文字の入力を扱う
not ok 7 - strtolower()は空の文字列をfooに変換する
#     Failed test (.\batch\test.php at line 21)
#            got: ''
#       expected: 'foo'
# Looks like you failed 1 tests of 7.
</code></pre>

<blockquote class="tip"><p>
  リスト15-1の始めの<code>include</code>ステートメントはオプションですが、<code>php test/unit/strtolowerTest.php</code>を呼び出すことで、テストファイルはsymfonyのコマンドラインを使わずに実行できる独立したPHPのスクリプトになります。</p>
</blockquote>

<a name="unit.testing.methods" id="unit.testing.methods"></a><h3>ユニットテストのメソッド</h3>

<p>テーブル15-2で一覧が示されるように、<code>lime_test</code>オブジェクトには多くのテストメソッドが付随しています。</p>

<p>テーブル15-2 - ユニットテストのための<code>lime_test</code>オブジェクトのメソッド</p>

<table>
<thead>
<tr>
  <th>メソッド</th>
  <th>説明</th>
</tr>
</thead>
<tbody>
<tr>
  <td><code>diag($msg)</code></td>
  <td>コメントを出力するがテストは実施しない</td>
</tr>
<tr>
  <td><code>ok($test, $msg)</code></td>
  <td>条件をテストしてtrueの場合にパスする</td>
</tr>
<tr>
  <td><code>is($value1, $value2, $msg)</code></td>
  <td>２つの値を比較して等しい場合にパスする</td>
</tr>
<tr>
  <td><code>isnt($value1, $value2, $msg)</code></td>
  <td>２つの値を比較し、等しくない場合にパスする</td>
</tr>
<tr>
  <td><code>like($string, $regexp, $msg)</code></td>
  <td>正規表現に対して文字列をテストする</td>
</tr>
<tr>
  <td><code>unlike($string, $regexp, $msg)</code></td>
  <td>文字列が正規表現にマッチしないことをチェックする</td>
</tr>
<tr>
  <td><code>cmp_ok($value1, $operator, $value2, $msg)</code></td>
  <td>演算子で引数を比較する</td>
</tr>
<tr>
  <td><code>isa_ok($variable, $type, $msg)</code></td>
  <td>引数のタイプをチェックする</td>
</tr>
<tr>
  <td><code>isa_ok($object, $class, $msg)</code></td>
  <td>オブジェクトのクラスをチェックする</td>
</tr>
<tr>
  <td><code>can_ok($object, $method, $msg)</code></td>
  <td>オブジェクトもしくはクラスのためのメソッドが利用できるかチェックする</td>
</tr>
<tr>
  <td><code>is_deeply($array1, $array2, $msg)</code></td>
  <td>同じ値を持つ2つの配列をチェックする</td>
</tr>
<tr>
  <td><code>include_ok($file, $msg)</code></td>
  <td>ファイルが存在し、適切に含まれるかをバリデートする</td>
</tr>
<tr>
  <td><code>fail()</code></td>
  <td>つねに失敗します--テストの例外に便利です</td>
</tr>
<tr>
  <td><code>pass()</code></td>
  <td>つねに成功します-- テストの例外に便利です</td>
</tr>
<tr>
  <td><code>skip($msg, $nb_tests)</code></td>
  <td><code>$nb_tests</code>件のテストをカウントします--条件つきのテストに便利です</td>
</tr>
<tr>
  <td><code>todo()</code></td>
  <td>テストとしてカウントします-- まだ書かれていないテストに便利です</td>
</tr>
</tbody>
</table>

<p>構文はとても単刀直入です; たいていのメソッドはメッセージを最後のパラメーターとして取ることに注意してください。このメッセージはテストが成功したときに出力に表示されます。これらのメソッドを学ぶベストな方法はこれらを実際にテストすることです。ですので、これらのメソッドをすべて使っているリスト15-3をご覧ください。</p>

<p>リスト15-3 - <code>lime_test</code>オブジェクトのメソッドをテストする(<code>test/unit/exampleTest.php</code>)</p>

<pre class="php"><span class="kw2">&lt;?php</span>
&nbsp;
<span class="kw1">include</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../bootstrap/unit.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// テストを目的としたスタブオブジェクトと関数</span>
<span class="kw2">class</span> myObject
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> myMethod<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">function</span> throw_an_exception<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="kw1">throw</span> <span class="kw2">new</span> Exception<span class="br0">&#40;</span><span class="st_h">'exception thrown'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="co1">// テストオブジェクトを初期化する</span>
<span class="re0">$t</span> <span class="sy0">=</span> <span class="kw2">new</span> lime_test<span class="br0">&#40;</span><span class="nu0">16</span><span class="sy0">,</span> <span class="kw2">new</span> lime_output_color<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">diag</span><span class="br0">&#40;</span><span class="st_h">'hello world'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">ok</span><span class="br0">&#40;</span><span class="nu0">1</span> <span class="sy0">==</span> <span class="st_h">'1'</span><span class="sy0">,</span> <span class="st_h">'等号演算子は型を無視する'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="nu0">1</span><span class="sy0">,</span> <span class="st_h">'1'</span><span class="sy0">,</span> <span class="st_h">'文字列は比較のために数字に変換される'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">isnt</span><span class="br0">&#40;</span><span class="nu0">0</span><span class="sy0">,</span> <span class="nu0">1</span><span class="sy0">,</span> <span class="st_h">'0と1は等しくない'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">like</span><span class="br0">&#40;</span><span class="st_h">'test01'</span><span class="sy0">,</span> <span class="st_h">'/test\d+/'</span><span class="sy0">,</span> <span class="st_h">'test01はテストの番号付けパターンに従う'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">unlike</span><span class="br0">&#40;</span><span class="st_h">'tests01'</span><span class="sy0">,</span> <span class="st_h">'/test\d+/'</span><span class="sy0">,</span> <span class="st_h">'tests01はこのパターンに従わない'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">cmp_ok</span><span class="br0">&#40;</span><span class="nu0">1</span><span class="sy0">,</span> <span class="st_h">'&lt;'</span><span class="sy0">,</span> <span class="nu0">2</span><span class="sy0">,</span> <span class="st_h">'1は2より小さい'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">cmp_ok</span><span class="br0">&#40;</span><span class="nu0">1</span><span class="sy0">,</span> <span class="st_h">'!=='</span><span class="sy0">,</span> <span class="kw4">true</span><span class="sy0">,</span> <span class="st_h">'1とtrueはまったく同じではない'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">isa_ok</span><span class="br0">&#40;</span><span class="st_h">'foobar'</span><span class="sy0">,</span> <span class="st_h">'string'</span><span class="sy0">,</span> <span class="st_h">'\'foobar\'は文字列'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">isa_ok</span><span class="br0">&#40;</span><span class="kw2">new</span> myObject<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'myObject'</span><span class="sy0">,</span> <span class="st_h">'new演算子は右のクラスのオブジェクトを作る'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">can_ok</span><span class="br0">&#40;</span><span class="kw2">new</span> myObject<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'myMethod'</span><span class="sy0">,</span> <span class="st_h">'myObjectクラスのオブジェクトはmyMethodメソッドを持つ'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$array1</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="nu0">1</span><span class="sy0">,</span> <span class="nu0">2</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="nu0">1</span> <span class="sy0">=&gt;</span> <span class="st_h">'foo'</span><span class="sy0">,</span> <span class="st_h">'a'</span> <span class="sy0">=&gt;</span> <span class="st_h">'4'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is_deeply</span><span class="br0">&#40;</span><span class="re0">$array1</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="nu0">1</span><span class="sy0">,</span> <span class="nu0">2</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="nu0">1</span> <span class="sy0">=&gt;</span> <span class="st_h">'foo'</span><span class="sy0">,</span> <span class="st_h">'a'</span> <span class="sy0">=&gt;</span> <span class="st_h">'4'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">,</span>
    <span class="st_h">'最初と2番目の配列は同じ'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">include_ok</span><span class="br0">&#40;</span><span class="st_h">'./fooBar.php'</span><span class="sy0">,</span> <span class="st_h">'fooBar.phpファイルが適切にインクルードされた'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
try
<span class="br0">&#123;</span>
  throw_an_exception<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">fail</span><span class="br0">&#40;</span><span class="st_h">'例外が投じられた後コードは実行されません'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
catch <span class="br0">&#40;</span>Exception <span class="re0">$e</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">pass</span><span class="br0">&#40;</span><span class="st_h">'例外の捕捉が成功しました'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="kw3">isset</span><span class="br0">&#40;</span><span class="re0">$foobar</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">skip</span><span class="br0">&#40;</span><span class="st_h">'テストの回数を正確に保つために1つのテストをスキップする'</span><span class="sy0">,</span> <span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
<span class="kw1">else</span>
<span class="br0">&#123;</span>
  <span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">ok</span><span class="br0">&#40;</span><span class="re0">$foobar</span><span class="sy0">,</span> <span class="st_h">'foobar'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">todo</span><span class="br0">&#40;</span><span class="st_h">'すべきテストが1つ残っている'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>symfonyのユニットテスト内にこれらのメソッドの使いかたの例が多く見つかります。</p>

<blockquote class="tip"><p>
  なぜ<code>ok()</code>と対照的に<code>is()</code>を使うのかとまどっているかもしれません。<code>is()</code>によるエラーメッセージの出力がはるかに明らかです; このメソッドがテストの両方のメンバを表示する一方で<code>ok()</code>は条件が失敗したことを伝えます。</p>
</blockquote>

<a name="testing.parameters" id="testing.parameters"></a><h3>パラメーターをテストする</h3>

<p><code>lime_test</code>オブジェクトの初期化は実行されるテストの数を最初のパラメーターとしてとります。最終的に実行されるテストの数がこのパラメーターの数値と異なる場合、limeはそのことに関する警告を出力します。たとえば、リスト15-3のテストセットはリスト15-4のように出力します。16回のテストが行われることを保証しましたが、実際には15回だけ行われたので、出力はこれを示してします。</p>

<p>リスト15-4 - テスト実行回数のカウントはテストの計画の助けになる</p>

<pre><code>&gt; symfony test-unit example

1..16
# hello world
ok 1 - 等号演算子は型を無視する
ok 2 - 文字列は比較のために数字に変換される
ok 3 - 0と1は等しくない
ok 4 - test01はテストの番号付けパターンに従う
ok 5 - tests01はこのパターンに従わない
ok 6 - 1は2より小さい
ok 7 - 1とtrueはまったく同じではない
ok 8 - 'foobar'は文字列
ok 9 - new演算子は右のクラスのオブジェクトを作る
ok 10 - myObjectクラスのオブジェクトはamyMethodメソッドを持つ
ok 11 - 最初と2番目の配列は同じ
not ok 12 - fooBar.phpファイルが適切にインクルードされた
#     Failed test (.\test\unit\testTest.php at line 27)
#       Tried to include './fooBar.php'
ok 13 - 例外の捕捉が成功しました
ok 14 # SKIP テストの回数を正確に保つために1つのテストをスキップする
ok 15 # TODO すべきテストが1つ残っている
# Looks like you planned 16 tests but only ran 15.
# Looks like you failed 1 tests of 16.
</code></pre>

<p><code>diag()</code>メソッドはテストとしてカウントされません。コメントを表示するためにこれを使用すれば、テストの出力は整理され読みやすい状態に保たれます。一方で、<code>todo()</code>メソッドと<code>skip()</code>メソッドは実際のテストとしてカウントされます。<code>try/catch</code>ブロック内の<code>pass()/fail()</code>メソッドの組み合わせは単独のテストとしてカウントされます。</p>

<p>よく計画されたテスト戦略は予想されるテストの数を含まなければなりません。とりわけテストが内部の条件もしくは例外の条件で動作する複雑なケースにおいて、テストの数が独自のテストファイルを検証するためにとても便利であることがわかるでしょう。そして、テストがある時点で失敗するとすぐにテストの数がわかります。実行テストの最後の数が初期化の間に渡された数字と一致しないからです。</p>

<p>コンストラクターの2番目のパラメーターは<code>lime_output</code>クラスを拡張する出力オブジェクトです。たいていの場合、テストはCLIを通して実行されることが前提なので、出力は<code>lime_output_color</code>オブジェクトで、利用可能であればbashの色付けを活用します。</p>

<a name="the.testunit.task" id="the.testunit.task"></a><h3>test-unitタスク</h3>

<p><code>test-unit</code>タスクは、コマンドラインから機能テストを起動させ、テストの名前のリストもしくはファイルのパターンを必要とします。リスト15-5で詳細をご覧ください。</p>

<p>リスト15-5 - 機能テストを起動させる</p>

<pre><code>// testディレクトリ構造
test/
  unit/
    myFunctionTest.php
    mySecondFunctionTest.php
    foo/
      barTest.php

&gt; symfony test-unit myFunction                   ## myFunctionTest.phpを実行する
&gt; symfony test-unit myFunction mySecondFunction  ## 両方のテストを実行する
&gt; symfony test-unit 'foo/*'                      ## barTest.phpを実行する
&gt; symfony test-unit '*'                          ## すべてのテストを実行する(再帰的)
</code></pre>

<a name="stubs.fixtures.and.autoloading" id="stubs.fixtures.and.autoloading"></a><h3>スタブ、フィクスチャ、オートロード</h3>

<p>ユニットテストにおいて、デフォルトではオートロード機能は有効ではありません。テストで使うそれぞれのクラスはテストファイルで定義するか、外部依存のファイルとしてrequireステートメントで読み込まなければなりません。リスト15-6で示されるように、多くのテストファイルが複数行の<code>include</code>ステートメントで始めるのはそういうわけです。</p>

<p>リスト15-6 - ユニットテストのクラスをインクルードする</p>

<pre class="php"><span class="kw2">&lt;?php</span>
&nbsp;
<span class="kw1">include</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../bootstrap/unit.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">include</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../../config/config.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">require_once</span><span class="br0">&#40;</span><span class="re0">$sf_symfony_lib_dir</span><span class="sy0">.</span><span class="st_h">'/util/sfToolkit.class.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$t</span> <span class="sy0">=</span> <span class="kw2">new</span> lime_test<span class="br0">&#40;</span><span class="nu0">7</span><span class="sy0">,</span> <span class="kw2">new</span> lime_output_color<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// isPathAbsolute()</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">diag</span><span class="br0">&#40;</span><span class="st_h">'isPathAbsolute()'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span>sfToolkit<span class="sy0">::</span><span class="me2">isPathAbsolute</span><span class="br0">&#40;</span><span class="st_h">'/test'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw4">true</span><span class="sy0">,</span>
    <span class="st_h">'isPathAbsolute()が絶対パスであるならtrueを返す'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span>sfToolkit<span class="sy0">::</span><span class="me2">isPathAbsolute</span><span class="br0">&#40;</span><span class="st_h">'\\test'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw4">true</span><span class="sy0">,</span>
    <span class="st_h">'isPathAbsolute()が絶対パスであるならtrueを返す'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span>sfToolkit<span class="sy0">::</span><span class="me2">isPathAbsolute</span><span class="br0">&#40;</span><span class="st_h">'C:\\test'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw4">true</span><span class="sy0">,</span>
    <span class="st_h">'isPathAbsolute()が絶対パスであるならtrueを返す'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span>sfToolkit<span class="sy0">::</span><span class="me2">isPathAbsolute</span><span class="br0">&#40;</span><span class="st_h">'d:/test'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw4">true</span><span class="sy0">,</span>
    <span class="st_h">'isPathAbsolute()が絶対パスであるならtrueを返す'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span>sfToolkit<span class="sy0">::</span><span class="me2">isPathAbsolute</span><span class="br0">&#40;</span><span class="st_h">'test'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw4">false</span><span class="sy0">,</span>
    <span class="st_h">'isPathAbsolute()が相対パスであるならfalseを返す'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span>sfToolkit<span class="sy0">::</span><span class="me2">isPathAbsolute</span><span class="br0">&#40;</span><span class="st_h">'../test'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw4">false</span><span class="sy0">,</span>
    <span class="st_h">'isPathAbsolute()が相対パスであるならfalseを返す'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span>sfToolkit<span class="sy0">::</span><span class="me2">isPathAbsolute</span><span class="br0">&#40;</span><span class="st_h">'..\\test'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw4">false</span><span class="sy0">,</span>
    <span class="st_h">'isPathAbsolute()が相対パスであるならfalseを返す'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>機能テストにおいて、テストしているオブジェクトだけをインスタンス化するだけでなく、依存するオブジェクトもインスタンス化する必要があります。機能テストは単一性を保たなければならないので、ほかのクラスに依存している場合1つのクラスが壊れると複数のテストが失敗する可能性があります。加えて、本当のオブジェクトをセットアップすることはコードの行数と実行時間の点から割高です。開発者は遅いプロセスにすぐに飽きるので、機能テストにおいてスピードが重大であることを覚えておいてください。</p>

<p>ユニットテストに対して多くのスクリプトをインクルードを始めるとき、シンプルなオートロードシステムが必要な場合があります。この目的のために、<code>sfCore</code>クラス(手動でインクルードしなければなりません)は絶対パスをパラメーターとして必要とする<code>initSimpleAutoload()</code>メソッドを提供します。このパスの元に設置されたすべてのクラスがオートロードされます。たとえば、<code>$sf_symfony_lib_dir/util/</code>の元に設置されたすべてのクラスをオートロードしたい場合、つぎのようなコードでユニットテストのスクリプトを始めてください。</p>

<pre class="php"><span class="kw1">require_once</span><span class="br0">&#40;</span><span class="re0">$sf_symfony_lib_dir</span><span class="sy0">.</span><span class="st_h">'/util/sfCore.class.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
sfCore<span class="sy0">::</span><span class="me2">initSimpleAutoload</span><span class="br0">&#40;</span><span class="re0">$sf_symfony_lib_dir</span><span class="sy0">.</span><span class="st_h">'/util'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<blockquote class="tip"><p>
  生成されたPropelのオブジェクトはクラスの長いカスケードに依存するので、Propelを動作させたいのであれば、<code>vendor/propel/</code>ディレクトリのもとでファイルをインクルードする必要もあります(<code>sfCore</code>への呼び出しは<code>sfCore::initSimpleAutoload(array(SF_ROOT_ DIR.'/lib/model', $sf_symfony_lib_dir.'/vendor/propel'));</code>になります)。そして(<code>set_include_path($sf_symfony_lib_dir.'/vendor'.PATH_SEPARATOR.SF_ROOT_DIR.PATH_SEPARATOR.get_include_path()</code>を呼び出すことで)Propelのコアをincludeのパスに追加する必要があります。</p>
</blockquote>

<p>オートロードの問題に関する別のよい次善策はスタブを利用することです。スタブ(stub)は本当のメソッドがシンプルであらかじめ用意されたデータに置き換わるクラスの代替の実装です。これは本当のクラスのふるまいを真似しますが、負担はかかりません。スタブのよい例はデータベースへの接続やWebサービスのインターフェイスです。リスト15-7において、APIマッピングのためのユニットテストは<code>WebService</code>クラスに依存します。実際のWebサービスクラスの本当の<code>fetch()</code>メソッドを呼び出す代わりに、テストはテストデータを返すスタブを使います。</p>

<p>リスト15-7 - ユニットテストでスタブを使う</p>

<pre class="php"><span class="kw1">require_once</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../../lib/WebService.class.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">require_once</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../../lib/MapAPI.class.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="kw2">class</span> testWebService <span class="kw2">extends</span> WebService
<span class="br0">&#123;</span>
  <span class="kw2">public</span> static <span class="kw2">function</span> fetch<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">return</span> <span class="kw3">file_get_contents</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/fixtures/data/fake_web_service.xml'</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="re0">$myMap</span> <span class="sy0">=</span> <span class="kw2">new</span> MapAPI<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$t</span> <span class="sy0">=</span> <span class="kw2">new</span> lime_test<span class="br0">&#40;</span><span class="nu0">1</span><span class="sy0">,</span> <span class="kw2">new</span> lime_output_color<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="re0">$myMap</span><span class="sy0">-&gt;</span><span class="me1">getMapSize</span><span class="br0">&#40;</span>testWebService<span class="sy0">::</span><span class="me2">fetch</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="nu0">100</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>テストデータは文字列やメソッドへの呼び出しよりも複雑になる可能性があります。複雑なテストデータはしばしフィクスチャ(fixture - 付属品)として参照されます。コーディングを明快にするために、とりわけフィクスチャが複数のユニットテストのファイルによって使われる場合、フィクスチャを別々のファイルに保存したほうがベターです。symfonyが<code>sfYAML::load()</code>メソッドによってYAMLファイルを配列に簡単に変換できることも忘れないでください。リスト15-8のように、このことはPHPの長い配列を書く代わりに、YAMLのファイルでテストデータを書くことができることを意味します。</p>

<p>リスト15-8 - ユニットテストでフィクスチャファイルを使う</p>

<pre class="php">// fixtures.ymlにて:
-
  input:   '/test'
  output:  true
  comment: isPathAbsolute()が絶対パスである場合trueを返す
-
  input:   '\\test'
  output:  true
  comment: isPathAbsolute()が絶対パスである場合trueを返す
-
  input:   'C:\\test'
  output:  true
  comment: isPathAbsolute()が絶対パスである場合trueを返す
-
  input:   'd:/test'
  output:  true
  comment: isPathAbsolute()が絶対パスである場合trueを返す
-
  input:   'test'
  output:  false
  comment: isPathAbsolute()が相対パスである場合falseを返す
-
  input:   '../test'
  output:  false
  comment: isPathAbsolute()が相対パスである場合falseを返す
-
  input:   '..\\test'
  output:  false
  comment: isPathAbsolute()が相対パスである場合falseを返す
&nbsp;
// testTest.phpにて
<span class="kw2">&lt;?php</span>
&nbsp;
<span class="kw1">include</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../bootstrap/unit.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">include</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../../config/config.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">require_once</span><span class="br0">&#40;</span><span class="re0">$sf_symfony_lib_dir</span><span class="sy0">.</span><span class="st_h">'/util/sfToolkit.class.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">require_once</span><span class="br0">&#40;</span><span class="re0">$sf_symfony_lib_dir</span><span class="sy0">.</span><span class="st_h">'/util/sfYaml.class.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$testCases</span> <span class="sy0">=</span> sfYaml<span class="sy0">::</span><span class="me2">load</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/fixtures.yml'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$t</span> <span class="sy0">=</span> <span class="kw2">new</span> lime_test<span class="br0">&#40;</span><span class="kw3">count</span><span class="br0">&#40;</span><span class="re0">$testCases</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw2">new</span> lime_output_color<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// isPathAbsolute()</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">diag</span><span class="br0">&#40;</span><span class="st_h">'isPathAbsolute()'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">foreach</span> <span class="br0">&#40;</span><span class="re0">$testCases</span> <span class="kw1">as</span> <span class="re0">$case</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span>sfToolkit<span class="sy0">::</span><span class="me2">isPathAbsolute</span><span class="br0">&#40;</span><span class="re0">$case</span><span class="br0">&#91;</span><span class="st_h">'input'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="re0">$case</span><span class="br0">&#91;</span><span class="st_h">'output'</span><span class="br0">&#93;</span><span class="sy0">,</span><span class="re0">$case</span><span class="br0">&#91;</span><span class="st_h">'comment'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre>

<a name="functional.tests" id="functional.tests"></a><h2>機能テスト</h2>

<p>機能テスト(functional test)はアプリケーションの一部を検証します。これらのテストは、アクションが想定された動作をするか手作業で検証する方法と同じように、ブラウジングセッションをシミュレートし、リクエストを作り、レスポンスの要素をチェックします。</p>

<a name="what.do.functional.tests.look.like" id="what.do.functional.tests.look.like"></a><h3>機能テストはどのように見えますか？</h3>

<p>テキストブラウザーと多くの正規表現で機能テストを実行できますが、時間の大きな無駄遣いです。symfonyは<code>sfBrowser</code>という名前の特別なオブジェクトを提供します。このオブジェクトは実際に必要なサーバーをともなわずにsymfonyのアプリケーションに接続したブラウザーのようにふるまいます。そしてHTTPの転送の減速は起きません。このオブジェクトはそれぞれのリクエスト(リクエスト、セッション、コンテキスト、レスポンスオブジェクト)のコアオブジェクトにアクセスできます。symfonyは<code>TestBrowser</code>と呼ばれるこのクラスの拡張機能も提供します。<code>sfTestBrowser</code>は機能テストのために設計され、<code>sfBrowser</code>オブジェクトのすべての機能に加えてスマートなアサートメソッドを持ちます。</p>

<p>機能テストは伝統的にテストブラウザーのオブジェクトを初期化することで始まります。このオブジェクトはリアクションへのレスポンスを作成し、レスポンス内に存在するいくつかの要素を変更します。</p>

<p>たとえば、<code>init-module</code>タスクもしくは<code>propel-init-crud</code>タスクでモジュールスケルトンを生成するたびに、symfonyはこのモジュールのためにシンプルな機能テストを作ります。テストはモジュールのデフォルトアクションにリクエストを行いレスポンスのステータスコード、ルーティングシステムによって算出されたモジュールとアクション、とレスポンスの内容のなかの特定のセンテンスの存在をチェックします。<code>foobar</code>モジュールに対して、生成された<code>foobarActionsTest.php</code>ファイルはリスト15-9のようになります。</p>

<p>リスト15-9 - 新しいモジュールのためのデフォルトの機能テスト(<code>tests/functional/frontend/foobarActionsTest.php</code>)</p>

<pre class="php"><span class="kw2">&lt;?php</span>
&nbsp;
<span class="kw1">include</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../../bootstrap/functional.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// 新しいテストブラウザーを作成する</span>
<span class="re0">$browser</span> <span class="sy0">=</span> <span class="kw2">new</span> sfTestBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$browser</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$browser</span><span class="sy0">-&gt;</span>
  <span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/foobar/index'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">isStatusCode</span><span class="br0">&#40;</span><span class="nu0">200</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">isRequestParameter</span><span class="br0">&#40;</span><span class="st_h">'module'</span><span class="sy0">,</span> <span class="st_h">'foobar'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">isRequestParameter</span><span class="br0">&#40;</span><span class="st_h">'action'</span><span class="sy0">,</span> <span class="st_h">'index'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">checkResponseElement</span><span class="br0">&#40;</span><span class="st_h">'body'</span><span class="sy0">,</span> <span class="st_h">'!/This is a temporary page/'</span><span class="br0">&#41;</span>
<span class="sy0">;</span></pre>

<blockquote class="tip"><p>
  ブラウザーのメソッドは<code>sfTestBrowser</code>オブジェクトを返すので、テストファイルをより読みやすくするにはメソッドチェーンを利用できます。これはオブジェクトへの流れるようなインターフェイス(fluid interfaceもしくはfluent interface)と呼ばれます。この名前の由来はメソッド呼び出しの流れを止めるものがないからです。</p>
</blockquote>

<p>機能テストはいくつかのリクエストと複雑なアサーションを含むことができます; つぎのセクションですべての機能を見ることになります。</p>

<p>機能テストを立ち上げるために、リスト15-10で示されるように、symfonyのコマンドラインで<code>test-functional</code>タスクを使います。このタスクはアプリケーションの名前とテストの名前を必要とします(<code>Test.php</code>のサフィックスを出力します)。</p>

<p>リスト15-10 - コマンドラインから1つの機能テストを立ち上げる</p>

<pre><code>&gt; symfony test-functional frontend foobarActions

# get /comment/index
ok 1 - status code is 200
ok 2 - request parameter module is foobar
ok 3 - request parameter action is index
not ok 4 - response selector body does not match regex /This is a temporary page/
# Looks like you failed 1 tests of 4.
1..4
</code></pre>

<p>新しいモジュールに対して生成された機能テストはデフォルトでは成功しません。新しく作成されたモジュールにおいて、<code>index</code>アクションは、"This is a temporary page."という文を含む初期ページにフォワードします(symfonyの<code>default</code>モジュールを含みます)。<code>index</code>アクションを修正しないかぎり、このモジュールに対するテストは失敗します。これは未終了のモジュールですべてのテストを成功できないことを保証します。</p>

<blockquote class="note"><p>
  機能テストにおいて、オートロードが有効なので、手動でファイルをインクルードする必要はありません。</p>
</blockquote>

<a name="browsing.with.the.sftestbrowser.object" id="browsing.with.the.sftestbrowser.object"></a><h3>sfBrowserオブジェクトでブラウジングする</h3>

<p>テストブラウザーは<code>GET</code>リクエストと<code>POST</code>リクエストを行う機能を持ちます。両方の場合において、本当のURIをパラメーターとして使います。リスト15-11はリクエストをシミュレートするために<code>sfTestBrowser</code>オブジェクトへの呼び出しを書く方法を示しています。</p>

<p>リスト15-11 - <code>sfTestBrowser</code>オブジェクトでリクエストをシミュレートする</p>

<pre class="php"><span class="kw1">include</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../../bootstrap/functional.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// 新しいテストブラウザーを作成する</span>
<span class="re0">$b</span> <span class="sy0">=</span> <span class="kw2">new</span> sfTestBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/foobar/show/id/1'</span><span class="br0">&#41;</span><span class="sy0">;</span>                   <span class="co1">// GETリクエスト</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">post</span><span class="br0">&#40;</span><span class="st_h">'/foobar/show'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'id'</span> <span class="sy0">=&gt;</span> <span class="nu0">1</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>     <span class="co1">// POSTリクエスト</span>
&nbsp;
<span class="co1">// get()メソッドとpost()メソッドはcall()メソッドへのショートカット</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">call</span><span class="br0">&#40;</span><span class="st_h">'/foobar/show/id/1'</span><span class="sy0">,</span> <span class="st_h">'get'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">call</span><span class="br0">&#40;</span><span class="st_h">'/foobar/show'</span><span class="sy0">,</span> <span class="st_h">'post'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'id'</span> <span class="sy0">=&gt;</span> <span class="nu0">1</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// call()メソッドは任意のメソッドによるリクエストをシミュレートする</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">call</span><span class="br0">&#40;</span><span class="st_h">'/foobar/show/id/1'</span><span class="sy0">,</span> <span class="st_h">'head'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">call</span><span class="br0">&#40;</span><span class="st_h">'/foobar/add/id/1'</span><span class="sy0">,</span> <span class="st_h">'put'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">call</span><span class="br0">&#40;</span><span class="st_h">'/foobar/delete/id/1'</span><span class="sy0">,</span> <span class="st_h">'delete'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>典型的なブラウジングセッションは特定のアクションへのリクエストだけでなく、リンクとブラウザーボタンへのクリックも含みます。リスト15-12で示されるように、<code>sfTestBrowser</code>オブジェクトはこれらもシミュレートできます。</p>

<p>リスト15-12 - <code>sfTestBrowser</code>オブジェクトでナビゲーションをシミュレートする</p>

<pre class="php"><span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/'</span><span class="br0">&#41;</span><span class="sy0">;</span>                  <span class="co1">// ホームページへのリクエスト</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/foobar/show/id/1'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">back</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>                    <span class="co1">// 履歴の1つのページに戻る</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">forward</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>                 <span class="co1">// 履歴の1つのページに進む</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">reload</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>                  <span class="co1">// 現在のページをリロードする</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">click</span><span class="br0">&#40;</span><span class="st_h">'go'</span><span class="br0">&#41;</span><span class="sy0">;</span>               <span class="co1">// 'go'リンクもしくはボタンを探してクリックする</span></pre>

<p>テストブラウザーは呼び出しのスタックを処理するので、<code>back()</code>メソッドと<code>forward()</code>メソッドは本当のブラウザー上と同じように動作します。</p>

<blockquote class="tip"><p>
  テストブラウザーはセッション(<code>sfTestStorage</code>)とCookieを管理する独自のメカニズムを持ちます。</p>
</blockquote>

<p>テストする必要のあるインタラクションのなかで、おそらくフォームに関連するものがもっとも優先されます。フォームの入力と投稿をシミュレートするには、選択肢が3つあります。送信したいパラメーターでPOSTリクエストを行う場合、配列としての<code>form</code>パラメーターで<code>click()</code>を呼び出すか、1つずつフィールドを入力して、投稿ボタンをクリックします。いずれにせよ、これらはすべて同じPOSTリクエストになります。リスト15-13は例を示しています。</p>

<p>リスト15-13 - <code>sfTestBrowser</code>オブジェクトでフォーム入力をシミュレートする</p>

<pre class="php">// modules/foobar/templates/editSuccess.phpでのテンプレートの例
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> form_tag<span class="br0">&#40;</span><span class="st_h">'foobar/update'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
  <span class="kw2">&lt;?php</span> <span class="kw1">echo</span> input_hidden_tag<span class="br0">&#40;</span><span class="st_h">'id'</span><span class="sy0">,</span> <span class="re0">$sf_params</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'id'</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
  <span class="kw2">&lt;?php</span> <span class="kw1">echo</span> input_tag<span class="br0">&#40;</span><span class="st_h">'name'</span><span class="sy0">,</span> <span class="st_h">'foo'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
  <span class="kw2">&lt;?php</span> <span class="kw1">echo</span> submit_tag<span class="br0">&#40;</span><span class="st_h">'go'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
  <span class="kw2">&lt;?php</span> <span class="kw1">echo</span> textarea<span class="br0">&#40;</span><span class="st_h">'text1'</span><span class="sy0">,</span> <span class="st_h">'foo'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
  <span class="kw2">&lt;?php</span> <span class="kw1">echo</span> textarea<span class="br0">&#40;</span><span class="st_h">'text2'</span><span class="sy0">,</span> <span class="st_h">'bar'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
&lt;/form&gt;
&nbsp;
// このフォームのための機能テストの例
$b = new sfTestBrowser();
$b-&gt;initialize();
$b-&gt;get('/foobar/edit/id/1');
&nbsp;
// オプション 1: POSTリクエスト
$b-&gt;post('/foobar/update', array('id' =&gt; 1, 'name' =&gt; 'dummy', 'commit' =&gt; 'go'));
&nbsp;
// オプション 2: パラメーターで投稿ボタンをクリックする
$b-&gt;click('go', array('name' =&gt; 'dummy'));
&nbsp;
// オプション 3: フィールド名でフォームの値を入力し投稿ボタンをクリックする
$b-&gt;setField('name', 'dummy')-&gt;
    click('go');</pre>

<blockquote class="note"><p>
  2番目と3番目のオプションによって、デフォルトのフォームの値は自動的にフォームの投稿に含まれ、フォームターゲットを指定する必要はありません。</p>
</blockquote>

<p><code>redirect()</code>メソッドによってアクションが終了した場合、テストブラウザーは自動的にリダイレクトされません; リスト15-14でお手本が示されるように、手動による<code>followRedirect()</code>メソッドでテストブラウザーをリダイレクトします。</p>

<p>リスト15-14 - テストブラウザーは自動的にリダイレクトされない</p>

<pre class="php"><span class="co1">// modules/foobar/actions/actions.class.phpのアクションの例</span>
<span class="kw2">public</span> <span class="kw2">function</span> executeUpdate<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="sy0">...</span>
  <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">redirect</span><span class="br0">&#40;</span><span class="st_h">'foobar/show?id='</span><span class="sy0">.</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getRequestParameter</span><span class="br0">&#40;</span><span class="st_h">'id'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="co1">// このアクションのための機能テストの例</span>
<span class="re0">$b</span> <span class="sy0">=</span> <span class="kw2">new</span> sfTestBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/foobar/edit?id=1'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">click</span><span class="br0">&#40;</span><span class="st_h">'go'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'name'</span> <span class="sy0">=&gt;</span> <span class="st_h">'dummy'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">isRedirected</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>   <span class="co1">// リクエストがリダイレクトされたかチェックする</span>
    followRedirect<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>    <span class="co1">// 手動でリダイレクトの後に続く</span></pre>

<p>ブラウジングのために便利なメソッドが1つ残っています。<code>restart()</code>はあたかもブラウザーを再起動したようにブラウジングの履歴、セッションとCookieを再び初期化します。</p>

<p>このメソッドが最初のリクエストを行うと、<code>sfTestBrowser</code>オブジェクトはリクエスト、コンテキスト、レスポンスオブジェクトにアクセスできます。テキストの内容からレスポンスヘッダー、リクエストパラメーターと設定まで及ぶ、多くの内容をチェックできます:</p>

<pre class="php"><span class="re0">$request</span>  <span class="sy0">=</span> <span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">getRequest</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$context</span>  <span class="sy0">=</span> <span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">getContext</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$response</span> <span class="sy0">=</span> <span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">getResponse</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<blockquote class="sidebar"><p class="title">
  <code>sfBrowser</code>オブジェクト</p>
  
  <p>リスト15-10から15-13まで説明されたすべてのブラウジングメソッドは<code>sfBrowser</code>オブジェクト全体に渡り、テストの範囲からも利用可能でつぎのように呼び出すことができます:</p>

<pre class="php"><span class="co1">// 新しいブラウザーを作成する</span>
<span class="re0">$b</span> <span class="sy0">=</span> <span class="kw2">new</span> sfBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/foobar/show/id/1'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">setField</span><span class="br0">&#40;</span><span class="st_h">'name'</span><span class="sy0">,</span> <span class="st_h">'dummy'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">click</span><span class="br0">&#40;</span><span class="st_h">'go'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$content</span> <span class="sy0">=</span> <span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">getResponse</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getContent</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="sy0">...</span></pre>
  
  <p>たとえば、それぞれのバッチスクリプトに対してキャッシュバージョンを生成するためにページのリストをブラウジングしたい場合、<code>sfBrowser</code>オブジェクトはバッチスクリプトのためにとても便利なツールです。(詳細な例に関しては18章を参照)。</p>
</blockquote>

<a name="using.assertions" id="using.assertions"></a><h3>アサーションを使う</h3>

<p>レスポンスとリクエストのほかのコンポーネントにアクセスできる<code>sfTestBrowser</code>オブジェクトのおかげで、これらのコンポーネント上でテストを実施できます。この目的のために新しい<code>lime_test</code>オブジェクトを作成できますが、幸いにして、<code>sfTestBrowser</code>は<code>lime_test</code>オブジェクトを返す<code>test()</code>メソッドを提示します。<code>sfTestBrowser</code>経由でアサーションを行う方法に関してはリスト15-15で確認してください。</p>

<p>リスト15-15 - テストブラウザーは<code>test()</code>メソッドによるテスト機能を提供する</p>

<pre class="php"><span class="re0">$b</span> <span class="sy0">=</span> <span class="kw2">new</span> sfTestBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/foobar/edit/id/1'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$request</span>  <span class="sy0">=</span> <span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">getRequest</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$context</span>  <span class="sy0">=</span> <span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">getContext</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$response</span> <span class="sy0">=</span> <span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">getResponse</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// test()メソッドを通してlime_testメソッドにアクセスする</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">test</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'id'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">test</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="re0">$response</span><span class="sy0">-&gt;</span><span class="me1">getStatuscode</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="nu0">200</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">test</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="re0">$response</span><span class="sy0">-&gt;</span><span class="me1">getHttpHeader</span><span class="br0">&#40;</span><span class="st_h">'content-type'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'text/html;charset=utf-8'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">test</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">like</span><span class="br0">&#40;</span><span class="re0">$response</span><span class="sy0">-&gt;</span><span class="me1">getContent</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'/edit/'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<blockquote class="note"><p>
  <code>getResponse()</code>、<code>getContent()</code>、<code>getRquest()</code>と、<code>test()</code>メソッドは<code>sfBrowser</code>オブジェクトを返さないので、これらのあとでは<code>sfTestBrwoser</code>メソッド呼び出しのチェーンを使用できません。</p>
</blockquote>

<p>リスト15-16で示されるように、リクエストオブジェクトとレスポンスオブジェクトを通して新旧のCookieをチェックできます。</p>

<p>リスト15-16 - <code>sfTestBrowser</code>でCookieをテストする</p>

<pre class="php"><span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">test</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getCookie</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'bar'</span><span class="br0">&#41;</span><span class="sy0">;</span>     <span class="co1">// 入ってくるCookie</span>
<span class="re0">$cookies</span> <span class="sy0">=</span> <span class="re0">$response</span><span class="sy0">-&gt;</span><span class="me1">getCookies</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">test</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="re0">$cookies</span><span class="br0">&#91;</span><span class="st_h">'foo'</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="st_h">'foo=bar'</span><span class="br0">&#41;</span><span class="sy0">;</span>            <span class="co1">// 出て行くCookie</span></pre>

<p>リクエストの要素をテストするために<code>test()</code>メソッドを使うと長い行のコードを書くことになります。幸いにして、<code>sfTestbrowser</code>オブジェクトは機能テストを読みやすく短く保つ一連のプロキシメソッドを含みます。さらに、これらのメソッドはこれら自身で<code>sfTestBrowser</code>オブジェクトを返します。たとえば、リスト15-17で示されるように、リスト15-15をより速い方法で書き換えることができます。</p>

<p>リスト15-17 - <code>sfTestBrowser</code>で直接テストする</p>

<pre class="php"><span class="re0">$b</span> <span class="sy0">=</span> <span class="kw2">new</span> sfTestBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/foobar/edit/id/1'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">isRequestParameter</span><span class="br0">&#40;</span><span class="st_h">'id'</span><span class="sy0">,</span> <span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">isStatusCode</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">isResponseHeader</span><span class="br0">&#40;</span><span class="st_h">'content-type'</span><span class="sy0">,</span> <span class="st_h">'text/html; charset=utf-8'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">responseContains</span><span class="br0">&#40;</span><span class="st_h">'edit'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>ステータス200は<code>isStatusCode()</code>メソッドによって求められるパラメーターのデフォルト値なので、連続したレスポンスをテストするために引数なしでこのメソッドを呼び出すことができます。</p>

<p>プロキシメソッドの利点は<code>lime_test</code>メソッドで出力テキストを指定する必要がないことです。メッセージはプロキシメソッドによって自動的に生成され、テストの出力は明快で読みやすいです。</p>

<pre><code># get /foobar/edit/id/1
ok 1 - request parameter "id" is "1"
ok 2 - status code is "200"
ok 3 - response header "content-type" is "text/html"
ok 4 - response contains "edit"
1..4
</code></pre>

<p>実際には、リスト15-17のプロキシメソッドは通常のテストの大部分をカバーするので、<code>sfTestBrowser</code>オブジェクト上で<code>test()</code>メソッドを使うことはめったにありません。</p>

<p>リスト15-14は<code>sfTestBrowser</code>は自動的にリダイレクトの後に続かないことを示しました。これは1つの利点を持ちます: リダイレクトをテストできることです。たとえば、リスト15-18はリスト15-14のレスポンスをテストする方法を示しています。</p>

<p>リスト15-18 - <code>sfTestBrowser</code>でリダイレクトをテストする</p>

<pre class="php"><span class="re0">$b</span> <span class="sy0">=</span> <span class="kw2">new</span> sfTestBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span>
    <span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/foobar/edit/id/1'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">click</span><span class="br0">&#40;</span><span class="st_h">'go'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'name'</span> <span class="sy0">=&gt;</span> <span class="st_h">'dummy'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">isStatusCode</span><span class="br0">&#40;</span><span class="nu0">200</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">isRequestParameter</span><span class="br0">&#40;</span><span class="st_h">'module'</span><span class="sy0">,</span> <span class="st_h">'foobar'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">isRequestParameter</span><span class="br0">&#40;</span><span class="st_h">'action'</span><span class="sy0">,</span> <span class="st_h">'update'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
    <span class="me1">isRedirected</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>      <span class="co1">// レスポンスがリダイレクトであることを確認する</span>
    followRedirect<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>    <span class="co1">// 手動でリダイレクトをフォローする     </span>
&nbsp;
    isStatusCode<span class="br0">&#40;</span><span class="nu0">200</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">isRequestParameter</span><span class="br0">&#40;</span><span class="st_h">'module'</span><span class="sy0">,</span> <span class="st_h">'foobar'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">isRequestParameter</span><span class="br0">&#40;</span><span class="st_h">'action'</span><span class="sy0">,</span> <span class="st_h">'show'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<a name="using.css.selectors" id="using.css.selectors"></a><h3>CSSセレクタを使う</h3>

<p>多くの機能テストはコンテンツ内にテキストが存在することを確認することでページが正しいかを検証します。<code>responseContains()</code>メソッド内で正規表現の助けを借りることで、表示されるテキスト、タグの属性、もしくは値をチェックできます。しかし、レスポンスのDOMに深く埋め込まれたものをチェックしたいのであれば、正規表現は理想的な方法ではありません。</p>

<p><code>sfTestBrowser</code>オブジェクトが<code>getResponseDom()</code>メソッドをサポートするわけはそういうわけです。これはlibXML2のDOMオブジェクトを返し、解析とテストの実行はフラットなテキストよりもはるかに簡単です。このメソッドの使いかたの例はリスト15-19をご覧ください。</p>

<p>リスト15-19 - テストブラウザーはDOMオブジェクトとしてレスポンスの内容にアクセスできる</p>

<pre class="php"><span class="re0">$b</span> <span class="sy0">=</span> <span class="kw2">new</span> sfTestBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/foobar/edit/id/1'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$dom</span> <span class="sy0">=</span> <span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">getResponseDom</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">test</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="re0">$dom</span><span class="sy0">-&gt;</span><span class="me1">getElementsByTagName</span><span class="br0">&#40;</span><span class="st_h">'input'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">item</span><span class="br0">&#40;</span><span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getAttribute</span><span class="br0">&#40;</span><span class="st_h">'type'</span><span class="br0">&#41;</span><span class="sy0">,</span><span class="st_h">'text'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>PHPのDOMメソッドによるHTMLのドキュメントの解析は十分な速さで行われずまた簡単でもありません。CSSセレクタに慣れているのであれば、これらのセレクタがHTMLのドキュメントから要素を読みとるためのより強力な方法であることをご存じでしょう。symfonyはDOMドキュメントをコンストラクターのパラメーターとして必要とする<code>sfDomCssSelector</code>と呼ばれるツールクラスを提供します。これはCSSセレクタにしたがって文字列の配列を返す<code>getTexts()</code>メソッドと、DOM要素の配列を返す<code>getElements()</code>メソッドを持ちます。リスト15-20の例をご覧ください。</p>

<p>リスト15-20 - テストブラウザーは<code>sfDomCssSelector</code>オブジェクトとしてのレスポンスの内容にアクセスできる</p>

<pre class="php"><span class="re0">$b</span> <span class="sy0">=</span> <span class="kw2">new</span> sfTestBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/foobar/edit/id/1'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$c</span> <span class="sy0">=</span> <span class="kw2">new</span> sfDomCssSelector<span class="br0">&#40;</span><span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">getResponseDom</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">test</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="re0">$c</span><span class="sy0">-&gt;</span><span class="me1">getTexts</span><span class="br0">&#40;</span><span class="st_h">'form input[type=&quot;hidden&quot;][value=&quot;1&quot;]'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">''</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">test</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="re0">$c</span><span class="sy0">-&gt;</span><span class="me1">getTexts</span><span class="br0">&#40;</span><span class="st_h">'form textarea[name=&quot;text1&quot;]'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">test</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="re0">$c</span><span class="sy0">-&gt;</span><span class="me1">getTexts</span><span class="br0">&#40;</span><span class="st_h">'form input[type=&quot;submit&quot;]'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">''</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>簡潔さと明瞭さを絶えず追求するために、symfonyはショートカットを提供します: <code>checkRsponseElement()</code>プロキシメソッドです。このメソッドはリスト15-20の内容をリスト15-21のようにします。</p>

<p>リスト15-21 - テストブラウザーはCSSセレクタによってレスポンス要素にアクセスできる</p>

<pre class="php"><span class="re0">$b</span> <span class="sy0">=</span> <span class="kw2">new</span> sfTestBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/foobar/edit/id/1'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">checkResponseElement</span><span class="br0">&#40;</span><span class="st_h">'form input[type=&quot;hidden&quot;][value=&quot;1&quot;]'</span><span class="sy0">,</span> <span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">checkResponseElement</span><span class="br0">&#40;</span><span class="st_h">'form textarea[name=&quot;text1&quot;]'</span><span class="sy0">,</span> <span class="st_h">'foo'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">checkResponseElement</span><span class="br0">&#40;</span><span class="st_h">'form input[type=&quot;submit&quot;]'</span><span class="sy0">,</span> <span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p><code>checkResponseElement()</code>メソッドのふるまいはそれが受けとる2番目の引数の型に依存します:</p>

<ul>
<li>ブール値の場合、CSSセレクタにマッチする要素が存在するかチェックをします。</li>
<li>整数の場合、CSSセレクタがこの数の結果を返すのかチェックをします。</li>
<li>正規表現の場合、CSSセレクタによって見つかる最初の要素がそれにマッチするかチェックをします。</li>
<li>!で始まる正規表現の場合、パターンにマッチしない最初の要素をチェックします。 </li>
<li>そのほかの場合、CSSセレクタで見つかる最初の要素と2番目の引数を文字列として比較します。</li>
</ul>

<p>メソッドは3番目のオプションパラメーターを連想配列の形式で受けとります。リスト15-22で示されるように、(セレクタがいくつかの要素を返す場合)セレクタによって返された最初の要素上ではなく、特定の位置上のほかの要素上でテストが実行されます。</p>

<p>リスト15-22 - 特定の位置で要素にマッチする位置オプションを使う</p>

<pre class="php"><span class="re0">$b</span> <span class="sy0">=</span> <span class="kw2">new</span> sfTestBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/foobar/edit?id=1'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">checkResponseElement</span><span class="br0">&#40;</span><span class="st_h">'form textarea'</span><span class="sy0">,</span> <span class="st_h">'foo'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">checkResponseElement</span><span class="br0">&#40;</span><span class="st_h">'form textarea'</span><span class="sy0">,</span> <span class="st_h">'bar'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'position'</span> <span class="sy0">=&gt;</span> <span class="nu0">1</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>オプションの配列は2つのテストを同時に実施するためにも使われます。リスト15-23で示されるように、セレクタが要素にマッチするかどうかとそれらが存在する数に関してテストできます。</p>

<p>リスト15-23 - マッチする数をカウントするcountオプションを使う</p>

<pre class="php"><span class="re0">$b</span> <span class="sy0">=</span> <span class="kw2">new</span> sfTestBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/foobar/edit?id=1'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">checkResponseElement</span><span class="br0">&#40;</span><span class="st_h">'form input'</span><span class="sy0">,</span> <span class="kw4">true</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'count'</span> <span class="sy0">=&gt;</span> <span class="nu0">3</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>セレクタのツールはとても強力です。これはCSS2.1のセレクタの大部分を受け入れ、リスト15-24のような複雑なクエリに対して利用できます。</p>

<p>リスト15-24 - <code>checkResponseElment()</code>によって受け入れられる複雑なCSSセレクタの例</p>

<pre class="php"><span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">checkResponseElement</span><span class="br0">&#40;</span><span class="st_h">'ul#list li a[href]'</span><span class="sy0">,</span> <span class="st_h">'click me'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">checkResponseElement</span><span class="br0">&#40;</span><span class="st_h">'ul &gt; li'</span><span class="sy0">,</span> <span class="st_h">'click me'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">checkResponseElement</span><span class="br0">&#40;</span><span class="st_h">'ul + li'</span><span class="sy0">,</span> <span class="st_h">'click me'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">checkResponseElement</span><span class="br0">&#40;</span><span class="st_h">'h1, h2'</span><span class="sy0">,</span> <span class="st_h">'click me'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">checkResponseElement</span><span class="br0">&#40;</span><span class="st_h">'a[class$=&quot;foo&quot;][href*=&quot;bar.html&quot;]'</span><span class="sy0">,</span> <span class="st_h">'my link'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<a name="working.in.the.test.environment" id="working.in.the.test.environment"></a><h3>テスト環境でとり組む</h3>

<p><code>sfTestBrowser</code>オブジェクトは<code>test</code>環境内で設定される特別なフロントコントローラーを使います。この環境に対するデフォルト設定はリスト15-25で表されます。</p>

<p>リスト15-25 - テスト環境のデフォルト設定(<code>myapp/config/settings.php</code>)</p>

<pre><code>test:
  .settings:
    # E_ALL | E_STRICT &amp; ~E_NOTICE = 2047
    error_reporting:        2047
    cache:                  off
    web_debug:              off
    no_script_name:         off
    etag:                   off
</code></pre>

<p>この環境においてキャッシュ(cache)とWebデバッグツールバー(web_debug)は<code>off</code>に設定されます。しかしながら、コードの実行は、<code>dev</code>環境と<code>prod</code>環境のログファイルは別にして、ログファイルにトレースされているので、それぞれのファイルを個別に確認できます(<code>myproject/log/myapp_test.log</code>)。この環境において、例外はスクリプトの実行を中止しません。1つのテストが失敗してもテスト全体のセットを実施できます。たとえば、テストデータを持つほかのデータベースを使うために、個別のデータベースの設定を持つことができます。</p>

<p><code>sfTestBrowser</code>オブジェクトは使うまえに初期化しなければなりません。必要であれば、アプリケーションのためのホスト名とクライアントのためのIPアドレスを指定できます。すなわち、これら2つのパラメーターを通してアプリケーションがコントロールを行う場合です。リスト15-26はこの方法を示しています。</p>

<p>リスト15-26 - ホスト名とIPでテストブラウザーをセットアップする</p>

<pre class="php"><span class="re0">$b</span> <span class="sy0">=</span> <span class="kw2">new</span> sfTestBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="st_h">'myapp.example.com'</span><span class="sy0">,</span> <span class="st_h">'123.456.789.123'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<a name="the.testfunctional.task" id="the.testfunctional.task"></a><h3>test-functionalタスクを使う</h3>

<p><code>test-functional</code>タスクによって1つもしくは複数の機能テストを実施することが可能で、このタスクは受けとる引数の数に依存します。リスト15-27で示されるように、機能テストが最初の引数としてアプリケーションの名前を必要とすること以外、ルールは<code>test-unit</code>タスクのものと同じになります。</p>

<p>リスト15-27 - 機能テストのタスク構文</p>

<pre><code>// testディレクトリの構造
test/
  functional/
    frontend/
      myModuleActionsTest.php
      myScenarioTest.php
    backend/
      myOtherScenarioTest.php

## 再帰的に、1つのアプリケーションに対してすべての機能テストを実行する
&gt; symfony test-functional frontend

## 1つの任意の機能テストを実行する
&gt; symfony test-functional frontend myScenario

## パターンに基づいていくつかのテストを実行する
&gt; symfony test-functional frontend my*
</code></pre>

<a name="test.naming.practices" id="test.naming.practices"></a><h2>テストの命名慣習</h2>

<p>このセクションではテストを整理して維持しやすい状態に保つためのいくつかの慣習の一覧を示します。使いこなすための秘訣はファイルの整理、ユニットテストと機能テストに関することです。</p>

<p>ファイル構造に関しては、テストする予定のクラス名で機能テストのファイルを名づけ、テストする予定のモジュールもしくはシナリオの名前でユニットテストを名づけます。例としてリスト15-28をご覧ください。<code>test/</code>ディレクトリはすぐに多くのファイルを含むようになるので、これらのガイドラインに従わないと、長い間にテストを見つけることが困難になる可能性があります。</p>

<p>リスト15-28 - ファイルの命名慣習の例</p>

<pre><code>test/
  unit/
    myFunctionTest.php
    mySecondFunctionTest.php
    foo/
      barTest.php
  functional/
    frontend/
      myModuleActionsTest.php
      myScenarioTest.php
    backend/
      myOtherScenarioTest.php
</code></pre>

<p>ユニットテストのためのよい習慣は関数もしくはメソッドによってテストを分類することと<code>diag()</code>呼び出しでそれぞれのテストのグループを始めることです。それぞれの機能テストのメッセージは関数の名前もしくは、テストされたメソッドを含み、動詞とプロパティの後に続くので、テストの出力はオブジェクトのプロパティを説明する文のように見えます。リスト15-29は例を示しています。</p>

<p>リスト15-29 - ユニットテストの命名慣習の例</p>

<pre class="php"><span class="co1">// srttolower()</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">diag</span><span class="br0">&#40;</span><span class="st_h">'strtolower()'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">isa_ok</span><span class="br0">&#40;</span><span class="kw3">strtolower</span><span class="br0">&#40;</span><span class="st_h">'Foo'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'string'</span><span class="sy0">,</span> <span class="st_h">'strtolower()は文字列を返す'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$t</span><span class="sy0">-&gt;</span><span class="me1">is</span><span class="br0">&#40;</span><span class="kw3">strtolower</span><span class="br0">&#40;</span><span class="st_h">'FOO'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st_h">'foo'</span><span class="sy0">,</span> <span class="st_h">'strtolower()は入力を小文字に変換する'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co2"># strtolower()
</span>ok <span class="nu0">1</span> <span class="sy0">-</span> <span class="kw3">strtolower</span><span class="br0">&#40;</span><span class="br0">&#41;</span>は文字列を返す
ok <span class="nu0">2</span> <span class="sy0">-</span> <span class="kw3">strtolower</span><span class="br0">&#40;</span><span class="br0">&#41;</span>は入力を小文字に変換する</pre>

<p>機能テストはページによって分類されリクエストによって始まります。リスト15-30はこの慣習を説明しています。</p>

<p>リスト15-30 - 機能テストの命名慣習の例</p>

<pre class="php"><span class="re0">$browser</span><span class="sy0">-&gt;</span>
  <span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/foobar/index'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">isStatusCode</span><span class="br0">&#40;</span><span class="nu0">200</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">isRequestParameter</span><span class="br0">&#40;</span><span class="st_h">'module'</span><span class="sy0">,</span> <span class="st_h">'foobar'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">isRequestParameter</span><span class="br0">&#40;</span><span class="st_h">'action'</span><span class="sy0">,</span> <span class="st_h">'index'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">checkResponseElement</span><span class="br0">&#40;</span><span class="st_h">'body'</span><span class="sy0">,</span> <span class="st_h">'/foobar/'</span><span class="br0">&#41;</span>
<span class="sy0">;</span>
&nbsp;
<span class="co2"># /comment/indexを取得する
</span>ok <span class="nu0">1</span> <span class="sy0">-</span> status code is <span class="nu0">200</span>
ok <span class="nu0">2</span> <span class="sy0">-</span> request parameter module is foobar
ok <span class="nu0">3</span> <span class="sy0">-</span> request parameter action is index
ok <span class="nu0">4</span> <span class="sy0">-</span> response selector body matches regex <span class="sy0">/</span>foobar<span class="sy0">/</span></pre>

<p>この規約に従えば、プロジェクトの開発者のドキュメントとして使うさいにテストの出力は十分に明快なものになります。そしていくつかの場合においてドキュメントを実際に書かなくてもすみます。</p>

<a name="special.testing.needs" id="special.testing.needs"></a><h2>特別なテストのニーズ</h2>

<p>たいていの場合、symfonyによって提供されたユニットテストと機能テストのツールで十分です。自動テストにおける共通の問題を解決するためのいくつかの補足のテクニックの一覧をこのセクションに書いておきます: 孤立した環境でテストの立ち上げ、テストの範囲以内でデータベースにアクセスし、キャッシュのテスト、クライアントサイド上でインタラクションのテストを行うことです。</p>

<a name="executing.tests.in.a.test.harness" id="executing.tests.in.a.test.harness"></a><h3>テストハーネスでテストを実行する</h3>

<p><code>test-unit</code>と<code>test-functional</code>タスクは単独のテストもしくはテストのセットを立ち上げることができます。しかしながら、これらのタスクをパラメーターなしで呼び出す場合、これらは<code>test/</code>ディレクトリ内に書かれたすべてのユニットテストと機能テストを立ち上げます。テストの間の汚染を回避するには、それぞれのテストファイルを独立したサンドボックスに分離する特定のメカニズムが必要です。さらに、(出力は何千行の長さになるので)その場合、単独のテストファイルのように同じ出力を続けることは無意味なので、テストの結果は統合的なビューにまとめられます。これが多くのテストファイルを実行するためにテストハーネスを使う理由です。テストハーネス(test harness)は特別な機能を持つ自動テストフレームワークです。テストハーネスは<code>lime_harness</code>と呼ばれるlimeフレームワークのコンポーネントに依存しています。リスト15-31のように、これはファイルごとのテストの状態と終了したテストの数の概要を示します。</p>

<p>リスト15-31 - テストハーネスですべてのテストを立ち上げる</p>

<pre><code>&gt; symfony test-all

unit/myFunctionTest.php................ok
unit/mySecondFunctionTest.php..........ok
unit/foo/barTest.php...................not ok

Failed Test                     Stat  Total   Fail  List of Failed
------------------------------------------------------------------
unit/foo/barTest.php               0      2      2  62 63
Failed 1/3 test scripts, 66.66% okay. 2/53 subtests failed, 96.22% okay.
</code></pre>

<p>テストは1つずつ呼び出すときと同じ方法で実行されます; 本当に便利にするために出力だけが短くなります。とりわけ、最後の表は失敗したテストに焦点を当てているので、これらのテストを見つけるための助けになります。</p>

<p>リスト15-32で示されるように、テストハーネスの<code>test-all</code>タスクを使うことですべてのテストを1つの呼び出しで起動できます。最新のリリース以降でリグレッション(回帰)が起こらないことを保証するために、この呼び出しはすべてのコードを製品環境に転送するまえに行うべきです。</p>

<p>リスト15-32 - プロジェクトのすべてのテストを立ち上げる</p>

<pre><code>&gt; symfony test-all
</code></pre>

<a name="accessing.a.database" id="accessing.a.database"></a><h3>データベースにアクセスする</h3>

<p>ユニットテストにおいてデータベースにアクセスすることがよく必要になります。最初に<code>sfTestBrowser::get()</code>を呼び出すときにデータベース接続は自動的に初期化されます。しかしながら、<code>sfTestBrowser</code>を使うまえにもデータベースに接続したい場合、リスト15-33のように、手動で<code>sfDabataseManager</code>を初期化しなければなりません。</p>

<p>リスト15-33 - テストにおいてデータベースを初期化する</p>

<pre class="php"><span class="re0">$databaseManager</span> <span class="sy0">=</span> <span class="kw2">new</span> sfDatabaseManager<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$databaseManager</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// オプションとして、現在のデータベース接続を取得できる</span>
<span class="re0">$con</span> <span class="sy0">=</span> Propel<span class="sy0">::</span><span class="me2">getConnection</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>テストを始めるまえにデータベースにフィクスチャを投入します。これは<code>sfPropelData</code>オブジェクトを通して行うことができます。リスト15-34で示されるように、<code>propel-load-data</code>タスクのように、ファイルからもしくは配列から、このオブジェクトはデータをロードします。</p>

<p>リスト15-34 - テストファイルからデータベースに投入する</p>

<pre class="php"><span class="re0">$data</span> <span class="sy0">=</span> <span class="kw2">new</span> sfPropelData<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// ファイルからデータをロードする</span>
<span class="re0">$data</span><span class="sy0">-&gt;</span><span class="me1">loadData</span><span class="br0">&#40;</span>sfConfig<span class="sy0">::</span><span class="me2">get</span><span class="br0">&#40;</span><span class="st_h">'sf_data_dir'</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/fixtures/test_data.yml'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// 配列からデータをロードする</span>
<span class="re0">$fixtures</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span>
  <span class="st_h">'Article'</span> <span class="sy0">=&gt;</span> <span class="kw3">array</span><span class="br0">&#40;</span>
    <span class="st_h">'article_1'</span> <span class="sy0">=&gt;</span> <span class="kw3">array</span><span class="br0">&#40;</span>
      <span class="st_h">'title'</span>      <span class="sy0">=&gt;</span> <span class="st_h">'foo title'</span><span class="sy0">,</span>
      <span class="st_h">'body'</span>       <span class="sy0">=&gt;</span> <span class="st_h">'bar body'</span><span class="sy0">,</span>
      <span class="st_h">'created_at'</span> <span class="sy0">=&gt;</span> <span class="kw3">time</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span>
    <span class="br0">&#41;</span><span class="sy0">,</span>
    <span class="st_h">'article_2'</span>    <span class="sy0">=&gt;</span> <span class="kw3">array</span><span class="br0">&#40;</span>
      <span class="st_h">'title'</span>      <span class="sy0">=&gt;</span> <span class="st_h">'foo foo title'</span><span class="sy0">,</span>
      <span class="st_h">'body'</span>       <span class="sy0">=&gt;</span> <span class="st_h">'bar bar body'</span><span class="sy0">,</span>
      <span class="st_h">'created_at'</span> <span class="sy0">=&gt;</span> <span class="kw3">time</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span>
    <span class="br0">&#41;</span><span class="sy0">,</span>
  <span class="br0">&#41;</span><span class="sy0">,</span>
<span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$data</span><span class="sy0">-&gt;</span><span class="me1">loadDataFromArray</span><span class="br0">&#40;</span><span class="re0">$fixtures</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>それから、あなたのテストのニーズに合わせて通常のアプリケーションのようにPropelオブジェクトを使います。これらのファイルをユニットテストにインクルードすることを覚えておいてください(この章の前のセクションの"スタブ、フィクスチャ、オートロード"で説明されているように、テストを自動化するために<code>sfCore::sfSimpleAutoloading()</code>メソッドを使用できます)。Propelオブジェクトは機能テストにオートロードされます。</p>

<a name="testing.the.cache" id="testing.the.cache"></a><h3>キャッシュをテストする</h3>

<p>アプリケーションに対してキャッシュを有効にしたとき、機能テストは期待どおりにキャッシュされたアクションが動作するか検証します。</p>

<p>最初に行うべきことはテスト環境(<code>settings.yml</code>ファイル)に対してキャッシュを有効にすることです。それから、ページがキャッシュから由来するものなのか、生成されたものであるのかをテストしたい場合、<code>sfTestBrowser</code>オブジェクトが提供する<code>isCached()</code>テストメソッドを使います。リスト15-35このメソッドの使いかたを示しています。</p>

<p>リスト15-35 - <code>isCached()</code>メソッドはキャッシュをテストする</p>

<pre class="php"><span class="kw2">&lt;?php</span>
&nbsp;
<span class="kw1">include</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../../bootstrap/functional.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// 新しいテストブラウザーを作成する</span>
<span class="re0">$b</span> <span class="sy0">=</span> <span class="kw2">new</span> sfTestBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">initialize</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/mymodule'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">isCached</span><span class="br0">&#40;</span><span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">;</span>       <span class="co1">// レスポンスがキャッシュからやって来たことを確認する</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">isCached</span><span class="br0">&#40;</span><span class="kw4">true</span><span class="sy0">,</span> <span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// キャッシュされたレスポンスがレイアウトと一緒に来ることを確認する</span>
<span class="re0">$b</span><span class="sy0">-&gt;</span><span class="me1">isCached</span><span class="br0">&#40;</span><span class="kw4">false</span><span class="br0">&#41;</span><span class="sy0">;</span>      <span class="co1">// レスポンスがキャッシュからやって来ないことを確認する</span></pre>

<blockquote class="note"><p>
  機能テストの最初にキャッシュをクリアする必要はありません; ブートストラップのスクリプトが代行してくれます。</p>
</blockquote>

<a name="testing.interactions.on.the.client" id="testing.interactions.on.the.client"></a><h3>クライアント上のインタラクションをテストする</h3>

<p>以前説明されたテクニックの主な難点はJavaScriptをシミュレートできないことです。たとえば、Ajaxインタラクションのようなとても複雑なインタラクションのために、ユーザーが行うマウスとキーボードの入力とクライアントサイド上でのスクリプトの実行を再現できることが必要です。通常、これらのテストは手作業で再現されますが、とても時間がかかりエラーになりがちです。</p>

<p>解決方法はSelenium(<a href="http://www.openqa.org/selenium/">http://www.openqa.org/selenium/</a>)と呼ばれるもので、完全にJavaScriptで書かれたテストフレームワークです。このツールは、現在のブラウザーウィンドウを利用して、通常のユーザーが行うようなページ上のアクションのセットを実行します。<code>sfBrowser</code>オブジェクトを越える利点はSlemeniumがページ内でJavaScriptを実行できるので、AjaxインタラクションもSlemeniumでテストできることです。</p>

<p>symfonyはSeleniumをデフォルトで搭載していません。これをインストールするには、<code>web/</code>ディレクトリ内に新しく<code>selenium/</code>ディレクトリを作り、Seleniumアーカイブの内容を展開する必要があります(<a href="http://www.openqa.org/selenium-core/download.action">http://www.openqa.org/selenium-core/download.action</a>)。なぜなら、SeleniumはJavaScriptに依存するので、たいていのブラウザー内のセキュリティ設定の基準に従えば、アプリケーションに関して同じホストとポート上でJavaScriptが利用できないかぎり、Seleniumの動作が許可されないからです。</p>

<blockquote class="caution"><p>
  <code>selenium/</code>ディレクトリを運用サーバーに直接転送しないように気をつけてください。ブラウザーを通してWebドキュメントのrootに誰でもアクセスできるからです。</p>
</blockquote>

<p>SeleniumテストはHTML形式で書かれ<code>web/slenium/tests/</code>ディレクトリに保存されます。たとえば、リスト15-36は、ホームページがロードされ、click meのリンクがクリックされ、レスポンス内で"Hello, World"のテキストが探される機能テストを示します。テスト環境内でアプリケーションにアクセスするには、<code>myapp_test.php</code>フロントコントローラーを指定する必要があります。</p>

<p>リスト15-36 - Seleniumテストのサンプル(<code>web/selenium/test/testIndex.html</code>)</p>

<pre class="php"><span class="sy0">&lt;!</span>DOCTYPE html <span class="kw2">PUBLIC</span> <span class="st0">&quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot;</span><span class="sy0">&gt;</span>
<span class="sy0">&lt;</span>html<span class="sy0">&gt;</span>
<span class="sy0">&lt;</span>head<span class="sy0">&gt;</span>
  <span class="sy0">&lt;</span>meta content<span class="sy0">=</span><span class="st0">&quot;text/html; charset=UTF-8&quot;</span> http<span class="sy0">-</span>equiv<span class="sy0">=</span><span class="st0">&quot;content-type&quot;</span><span class="sy0">&gt;</span>
  <span class="sy0">&lt;</span>title<span class="sy0">&gt;</span>Index tests<span class="sy0">&lt;/</span>title<span class="sy0">&gt;</span>
<span class="sy0">&lt;/</span>head<span class="sy0">&gt;</span>
<span class="sy0">&lt;</span>body<span class="sy0">&gt;</span>
<span class="sy0">&lt;</span>table cellspacing<span class="sy0">=</span><span class="st0">&quot;0&quot;</span><span class="sy0">&gt;</span>
<span class="sy0">&lt;</span>tbody<span class="sy0">&gt;</span>
  <span class="sy0">&lt;</span>tr<span class="sy0">&gt;&lt;</span>td colspan<span class="sy0">=</span><span class="st0">&quot;3&quot;</span><span class="sy0">&gt;</span>First step<span class="sy0">&lt;/</span>td<span class="sy0">&gt;&lt;/</span>tr<span class="sy0">&gt;</span>
  <span class="sy0">&lt;</span>tr<span class="sy0">&gt;&lt;</span>td<span class="sy0">&gt;</span>open<span class="sy0">&lt;/</span>td<span class="sy0">&gt;</span>              <span class="sy0">&lt;</span>td<span class="sy0">&gt;/</span>myapp_test<span class="sy0">.</span>php<span class="sy0">/&lt;/</span>td<span class="sy0">&gt;</span> <span class="sy0">&lt;</span>td<span class="sy0">&gt;&amp;</span>nbsp<span class="sy0">;&lt;/</span>td<span class="sy0">&gt;&lt;/</span>tr<span class="sy0">&gt;</span>
  <span class="sy0">&lt;</span>tr<span class="sy0">&gt;&lt;</span>td<span class="sy0">&gt;</span>clickAndWait<span class="sy0">&lt;/</span>td<span class="sy0">&gt;</span>      <span class="sy0">&lt;</span>td<span class="sy0">&gt;</span>link<span class="sy0">=</span>click me<span class="sy0">&lt;/</span>td<span class="sy0">&gt;</span>    <span class="sy0">&lt;</span>td<span class="sy0">&gt;&amp;</span>nbsp<span class="sy0">;&lt;/</span>td<span class="sy0">&gt;&lt;/</span>tr<span class="sy0">&gt;</span>
  <span class="sy0">&lt;</span>tr<span class="sy0">&gt;&lt;</span>td<span class="sy0">&gt;</span>assertTextPresent<span class="sy0">&lt;/</span>td<span class="sy0">&gt;</span> <span class="sy0">&lt;</span>td<span class="sy0">&gt;</span>Hello<span class="sy0">,</span> World<span class="sy0">!&lt;/</span>td<span class="sy0">&gt;</span>    <span class="sy0">&lt;</span>td<span class="sy0">&gt;&amp;</span>nbsp<span class="sy0">;&lt;/</span>td<span class="sy0">&gt;&lt;/</span>tr<span class="sy0">&gt;</span>
<span class="sy0">&lt;/</span>tbody<span class="sy0">&gt;</span>
<span class="sy0">&lt;/</span>table<span class="sy0">&gt;</span>
<span class="sy0">&lt;/</span>body<span class="sy0">&gt;</span>
<span class="sy0">&lt;/</span>html<span class="sy0">&gt;</span></pre>

<p>テストケースはコマンド、ターゲット、値の3つのカラムを持つテーブルを含むHTMLドキュメントによって表現されます。すべてのコマンドは値をとりません。コマンドが値を取らない場合、カラムを空白にしておくか、テーブルを見やすくするために<code>&amp;nbsp;</code>を使うことです。コマンドの完全な一覧はSeleniumのWebサイトを参照してください。</p>

<p>同じディレクトリに設置された<code>TestSuite.html</code>ファイル内に新しい行を挿入することで、このテストをグローバルテストスイートに追加する必要があります。リスト15-37はこれを行う方法を示しています。</p>

<p>リスト15-37 - テストファイルをテストスイートに追加する(<code>web/selenium/test/TestSuite.html</code>)</p>

<pre><code>...
&lt;tr&gt;&lt;td&gt;&lt;a href='./testIndex.html'&gt;My First Test&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
...
</code></pre>

<p>テストを実行するために、つぎのURLにブラウザーでアクセスしてください。</p>

<pre><code>http://myapp.example.com/selenium/index.html
</code></pre>

<p>Main Test Suiteを選択し、すべてのテストを実行するボタンをクリックし、行うように伝えられたステップをブラウザーが再現する様子を観察してください。</p>

<blockquote class="note"><p>
  Seleniumのテストは本当のブラウザーで動作するので、これらによってブラウザーの不一致もテストできます。1つのブラウザーでテストを作り、単独のリクエストで動作することになっているサイト上でそのほかのすべてのブラウザー上でSeleniumのテストを実施してください。</p>
</blockquote>

<p>SelenimはHTMLで書かれているので、Seleniumのテストを書くことは面倒でした。しかし、FirefoxのSelenium拡張機能のおかげで(<a href="http://seleniumrecorder.mozdev.org/">http://seleniumrecorder.mozdev.org/</a>)、テストを実施するために必要なことはレコードセッションで1回のテストを実施するだけです。レコードセッションでナビゲートする一方で、ブラウザーのウィンドウ内で右クリックをしてポップアップメニュー内のAppend Selenium Commandのもとで適切なチェック項目を選択することで、アサート型のテストを追加できます。</p>

<p>アプリケーションに対してテストスイートを実施するためにテストをHTMLのファイルに保存できます。Firefoxの拡張機能によって記録したSeleniumテストも実行できるようになります。</p>

<blockquote class="note"><p>
  Seleniumテストを立ち上げるまえにテストデータを再び初期化することを忘れないでください。</p>
</blockquote>

<a name="summary" id="summary"></a><h2>まとめ</h2>

<p>自動テストとしてメソッドもしくは関数を検証するユニットテスト(unit test)と機能を検証する機能テスト(functional test)が存在します。symfonyはユニットテストのためのlimeテストフレームワークに依存し、ユニットテスト用に特化した<code>sfTestBrowser</code>クラスを提供します。これらのテストツールは、CSSセレクタのように、両方とも基礎から応用まで及ぶ多くのアサーションメソッドを提供します。テストを起動させるにはsymfonyのコマンドラインを使います。1つずつ実施するには<code>test-unit</code>タスクもしくは<code>test-functional</code>タスクを使い、テストハーネスを実施するには<code>test-all</code>タスクを使います。データを扱うとき、自動テストはフィクスチャ(fixture)とスタブ(stub)を使い、これはsymfonyのユニットテスト内で簡単に実現されます。</p>

<p>(おそらくテスト駆動開発(TDD)の方法論を利用して)アプリケーションの大部分をカバーするために十分なユニットテストをかならず書けば、内部をリファクタリングするもしくは新しい機能を追加するときに、安心感を得られドキュメントを作るための時間を節約することもできます。</p>
</div>
<div class="navigation">
<hr/>
<table width="100%">
<tr>
<td width="40%" align="left"><a href="14-Generators.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="16-Application-Management-Tools.html">次の章</a></td>
</tr>
</table>

</div>
</body>

</html>
